# coding=utf-8
import ctypes
import inspect
import os
import sys
import json
import time
from ctypes import *
from socket import AF_INET, socket, SOCK_STREAM
from threading import Thread, Lock
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QFont
from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QVBoxLayout, QPushButton, QDesktopWidget, QLineEdit, QMessageBox
from flask import Flask, Response

mutex = Lock()
app = Flask(__name__)
win = None
dll = windll.LoadLibrary("./Termb.dll")
service = None
readerStatus = False
serviceStatusU = 1
PORT = 15000
HOST = "*"

krz = {
    1: "卡片认证成功",
    2: "寻卡失败",
    3: "选卡失败",
    4: "未连接读卡器",
    0: "动态库未加载",
}

dk = {
    1: "正确",
    0: "读身份证失败",
    4: "未连接读卡器",
    99: "动态库未加载",
}

serviceStatus = {
    1: "未启动",
    3: "运行中",
    5: "已停止",
    7: "未连接读卡器",
    9: "端口被占用"
}

infoList = [
    {"name": "姓名", "function": "GetPeopleName", "param": "partyName", "size": 30},
    {"name": "性别", "function": "GetPeopleSex", "param": "gender", "size": 2},
    {"name": "民族", "function": "GetPeopleNation", "param": "nation", "size": 20},
    {"name": "出生日期", "function": "GetPeopleBirthday", "param": "bornDay", "size": 16},
    {"name": "身份证号", "function": "GetPeopleIDCode", "param": "certNumber", "size": 36},
    {"name": "发证机关", "function": "GetDepartment", "param": "certOrg", "size": 30},
    {"name": "有效开始日期", "function": "GetStartDate", "param": "effDate", "size": 16},
    {"name": "有效截止日期", "function": "GetEndDate", "param": "expDate", "size": 16},
    {"name": "证件类别", "function": "GetCertType", "param": "certType", "size": 1},
    {"name": "地址信息", "function": "GetPeopleAddress", "param": "certAddress", "size": 70},
    {"name": "追加地址", "function": "GetNewAppMsg", "param": "newAppMsg", "size": 70},
    {"name": "头像照片", "function": "Getbase64JpgData", "param": "identityPic", "size": 77736},
]


def readerInit():
    port = c_int32(1001)
    status = dll.CVR_InitComm(port)
    if status == 1:
        return True
    return False


def readerTestInit():
    global serviceStatusU, win
    if readerInit() == False:
        serviceStatusU = 7
        win.updateStatus()
        return False
    else:
        status = dll.CVR_CloseComm()
        if status == 1:
            return True
        return False


def portTest():
    global serviceStatusU, win, PORT
    ip = "127.0.0.1"
    port = PORT
    s = socket(AF_INET, SOCK_STREAM)
    try:
        s.connect((ip, int(port)))
        s.shutdown(2)
        serviceStatusU = 9
        win.updateStatus()
        return False
    except:
        return True


def webServiceStart():
    global service, win, serviceStatusU, PORT, HOST
    if (serviceStatusU == 3):
        return
    serviceStatusU = 3
    if not portTest():
        return
    if not readerTestInit():
        return
    service = Thread(target=flaskStart, kwargs={'port': PORT, 'host': HOST})
    service.start()
    time.sleep(2)
    win.updateStatus()


def _async_raise(tid, exctype):
    tid = ctypes.c_long(tid)
    if not inspect.isclass(exctype):
        exctype = type(exctype)
    res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exctype))
    if res == 0:
        raise ValueError("invalid thread id")
    elif res != 1:
        ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, None)
        raise SystemError("PyThreadState_SetAsyncExc failed")


def stop_thread(thread):
    _async_raise(thread.ident, SystemExit)

def webServiceStop():
    global service, serviceStatusU, win
    if (serviceStatusU == 5):
        return
    serviceStatusU = 5
    if service:
        stop_thread(service)
        service = None
        win.updateStatus()


def webServiceRestart():
    global service, serviceStatusU, win
    if (serviceStatusU != 3):
        return
    webServiceStop()
    time.sleep(3)
    webServiceStart()


def flaskStart(port, host):
    global HOST
    HOST = host
    readerInit()
    app.run(port=port)


def readRealData():
    result = {}
    result["resultFlag"] = -1
    result["errorMsg"] = ""

    dev_ret = dll.CVR_Authenticate()
    if dev_ret == 4:
        readerInit()
        dev_ret = dll.CVR_Authenticate()
    result["errorMsg"] = "卡认证" + krz.get(dev_ret, "未知错误")
    if dev_ret == 1:
        card_ret = dll.CVR_Read_FPContent()
        if dev_ret == 4:
            readerInit()
            dev_ret = dll.CVR_Authenticate()
            card_ret = dll.CVR_Read_FPContent()
        result["errorMsg"] = "读卡" + dk.get(card_ret, "未知错误")
        if card_ret == 1:
            result["resultFlag"] = 0
            for item in infoList:
                res = create_string_buffer(item.get("size", 0))
                len = c_int32(0)
                status = eval("dll." + item.get("function", ""))(res, byref(len))
                result[item.get("param", "")] = string_at(res).decode("gbk")
                res = None
            result["queryTime"] = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
    return result


@app.route('/readcard')
def readcard():
    if mutex.acquire(blocking=False):
        res = readRealData()
        mutex.release()
    else:
        res = {
            "resultFlag": -1,
            "errorMsg": "设备繁忙"
        }
    return Response(
        json.dumps(res, ensure_ascii=False),
        mimetype='application/json',
        headers={"Access-Control-Allow-Origin": HOST}
    )


@app.route('/getsamid')
def getsamid():
    if mutex.acquire(blocking=False):
        res = {"resultFlag": 0, "errorMsg": "获取成功"}
        samid = create_string_buffer(100)
        status = dll.CVR_GetSAMID(samid)
        samid_res = string_at(samid).decode("gbk")
        samid = None
        if samid_res != "":
            res["samID"] = samid_res
        mutex.release()
    else:
        res = {
            "resultFlag": -1,
            "errorMsg": "设备繁忙"
        }
    return Response(
        json.dumps(res, ensure_ascii=False),
        mimetype='application/json',
        headers={"Access-Control-Allow-Origin": HOST}
    )


@app.route('/')
def index():
    return "欢迎使用身份证阅读程序"


class MyWindow(QWidget):

    def __init__(self):
        super().__init__()
        self.title = None
        self.hosttitle = None
        self.host = None
        self.porttitle = None
        self.port = None
        self.statustitle = None
        self.status = None
        self.startbtn = None
        self.stopbtn = None
        self.restartbtn = None
        self.vbox = None
        self.initUI()

    def initUI(self):
        self.setWindowTitle('身份证阅读器服务')
        self.resize(500, 370)
        self.setCenter()
        self.setFixedSize(self.width(), self.height())

        self.title = QLabel(self)
        self.title.setAutoFillBackground(True)
        self.title.setText("二代居民身份证阅读服务")
        self.title.setFixedHeight(50)
        self.title.setFont(QFont("simsong", 20, QFont.Bold))
        self.title.setAlignment(Qt.AlignCenter)
        self.title.setOpenExternalLinks(True)
        self.title.move(0, 0)

        self.hosttitle = QLabel(self)
        self.hosttitle.setText("调用域名:")
        self.hosttitle.setFixedHeight(25)
        self.hosttitle.setFixedWidth(88)
        self.hosttitle.setFont(QFont("微软雅黑", 10, QFont.Courier))
        self.hosttitle.setAlignment(Qt.AlignCenter)
        self.hosttitle.move(12, 80)

        self.host = QLineEdit(self)
        self.host.setText(HOST)
        self.host.setFixedWidth(300)
        self.host.setFixedHeight(25)
        self.host.move(100, 80)

        self.porttitle = QLabel(self)
        self.porttitle.setText("服务端口:")
        self.porttitle.setFixedHeight(25)
        self.porttitle.setFixedWidth(88)
        self.porttitle.setFont(QFont("微软雅黑", 10, QFont.Courier))
        self.porttitle.setAlignment(Qt.AlignCenter)
        self.porttitle.move(12, 115)

        self.port = QLineEdit(self)
        self.port.setText(str(PORT))
        self.port.setFixedWidth(300)
        self.port.setFixedHeight(25)
        self.port.move(100, 115)

        self.statustitle = QLabel(self)
        self.statustitle.setText("服务状态:")
        self.statustitle.setFixedHeight(25)
        self.statustitle.setFixedWidth(88)
        self.statustitle.setFont(QFont("微软雅黑", 10, QFont.Courier))
        self.statustitle.setAlignment(Qt.AlignCenter)
        self.statustitle.move(12, 150)

        self.status = QLabel(self)
        self.status.setText("服务已启动")
        self.status.setFixedHeight(25)
        self.status.setFixedWidth(200)
        self.status.setFont(QFont("微软雅黑", 10, QFont.Bold))
        self.status.setLineWidth(25)
        self.status.move(100, 150)

        self.startbtn = QPushButton('启动', self)
        self.startbtn.resize(100, 70)
        self.startbtn.clicked.connect(lambda: self.start())
        self.startbtn.setFont(QFont("微软雅黑", 12, QFont.Courier))
        self.startbtn.move(25, 200)

        self.stopbtn = QPushButton('停止', self)
        self.stopbtn.resize(100, 70)
        self.stopbtn.clicked.connect(lambda: self.stop())
        self.stopbtn.setFont(QFont("微软雅黑", 12, QFont.Courier))
        self.stopbtn.move(200, 200)

        self.restartbtn = QPushButton('重启', self)
        self.restartbtn.resize(100, 70)
        self.restartbtn.clicked.connect(lambda: self.restart())
        self.restartbtn.setFont(QFont("微软雅黑", 12, QFont.Courier))
        self.restartbtn.move(375, 200)

        self.footer = QLabel(self)
        self.footer.setText("注意：本软件仅适配华视科技CRV-100系列读卡器。\n技术支持：内蒙古灵越科技有限公司 0471-5610622")
        self.footer.setOpenExternalLinks(True)
        self.footer.setFixedHeight(50)
        self.footer.setFixedWidth(500)
        self.footer.setFont(QFont("微软雅黑", 10, QFont.Courier))
        self.footer.move(25, 315)

        self.testbtn = QPushButton('驱动安装', self)
        self.testbtn.resize(80, 25)
        self.testbtn.clicked.connect(lambda: self.install())
        self.testbtn.setFont(QFont("微软雅黑", 8, QFont.Courier))
        self.testbtn.move(25, 285)

        self.testbtn = QPushButton('驱动测试', self)
        self.testbtn.resize(80, 25)
        self.testbtn.clicked.connect(lambda: self.test())
        self.testbtn.setFont(QFont("微软雅黑", 8, QFont.Courier))
        self.testbtn.move(125, 285)

        self.vbox = QVBoxLayout()
        self.setLayout(self.vbox)
        self.vbox.addWidget(self.title)
        self.vbox.addStretch()

    def setCenter(self):
        screen = QDesktopWidget().screenGeometry()
        size = self.geometry()
        self.move(int((screen.width() - size.width()) / 2), int((screen.height() - size.height()) / 2))

    def start(self):
        global PORT, HOST
        port = self.port.text()
        HOST = self.host.text()
        if not port.isdigit():
            QMessageBox.warning(self, '警告', "端口号错误！")
            return
        port = int(port)
        if (port < 1 or port > 65535):
            QMessageBox.warning(self, '警告', "端口号错误！")
            return
        PORT = port
        webServiceStart()

    def stop(self):
        webServiceStop()

    def restart(self):
        webServiceRestart()

    def test(self):
        os.system("Test.exe")

    def install(self):
        os.system("msiexec /i USBDrv3.0-x64.msi")

    def updateStatus(self):
        status = serviceStatusU
        self.status.setText(serviceStatus.get(status, "服务异常"))

    def closeEvent(self, event):
        if serviceStatusU == 3:
            reply = QMessageBox.warning(self, '警告', "服务正在运行中，请停止后退出！")
            if reply == QMessageBox.Yes:
                event.accept()
            else:
                event.ignore()


def UIStart():
    global win
    app = QApplication(sys.argv)
    win = MyWindow()
    win.show()
    win.updateStatus()
    sys.exit(app.exec_())


if __name__ == '__main__':
    UIStart()
